<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JSON转TypeScript接口工具</title>
    <!-- 添加 highlight.js 用于代码高亮 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/typescript.min.js"></script>
    <!-- 添加 CodeMirror -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/javascript/javascript.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/edit/matchbrackets.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/edit/closebrackets.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/fold/foldcode.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/fold/foldgutter.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/fold/brace-fold.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/fold/indent-fold.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/fold/foldgutter.min.css">
    <style>
      body {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
        margin: 0;
        padding: 20px;
        background-color: #f5f5f5;
      }
      .container {
        display: flex;
        gap: 20px;
        height: calc(100vh - 40px);
        max-width: 1400px;
        margin: 0 auto;
      }
      .editor-box {
        flex: 1;
        background: white;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
        display: flex;
        flex-direction: column;
      }
      .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 15px;
      }
      .options {
        display: flex;
        gap: 10px;
        align-items: center;
      }
      .CodeMirror {
        height: 100% !important;
        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
        font-size: 14px;
        line-height: 1.5;
        border: 1px solid #ddd;
        border-radius: 6px;
      }
      .output-container {
        flex: 1;
        position: relative;
        overflow: auto;
      }
      pre {
        margin: 0;
        border-radius: 6px;
        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
        font-size: 14px;
        line-height: 1.5;
      }
      pre code {
        padding: 15px !important;
      }
      .error {
        color: #dc3545;
        margin-top: 10px;
        font-size: 14px;
      }
      h2 {
        margin: 0;
        color: #2c3e50;
        font-size: 18px;
      }
      input[type="text"] {
        padding: 8px 12px;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
        width: 200px;
      }
      button {
        padding: 8px 16px;
        background: #4CAF50;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        transition: background 0.2s;
      }
      button:hover {
        background: #45a049;
      }
      .copy-button {
        position: absolute;
        top: 10px;
        right: 10px;
        z-index: 10;
        background: #666;
        padding: 6px 12px;
        font-size: 12px;
      }
      .copy-button:hover {
        background: #555;
      }
      .beautify-button {
        background: #2196F3;
      }
      .beautify-button:hover {
        background: #1976D2;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="editor-box">
        <div class="header">
          <h2>JSON输入</h2>
          <div class="options">
            <input type="text" id="interfaceName" value="RootObject" placeholder="接口名称">
            <select id="inputType" style="padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
              <option value="json">JSON</option>
              <option value="js">JavaScript对象</option>
            </select>
            <button class="beautify-button" onclick="beautifyInput()">格式化</button>
          </div>
        </div>
        <div id="editor"></div>
        <div id="errorMessage" class="error"></div>
      </div>
      <div class="editor-box">
        <div class="header">
          <h2>TypeScript接口</h2>
        </div>
        <div class="output-container">
          <button class="copy-button" onclick="copyToClipboard()">复制代码</button>
          <pre><code class="language-typescript" id="tsOutput"></code></pre>
        </div>
      </div>
    </div>

    <script>
      let editor;
      
      // 初始化 CodeMirror
      document.addEventListener('DOMContentLoaded', function() {
        editor = CodeMirror(document.getElementById("editor"), {
          mode: "javascript",
          theme: "monokai",
          lineNumbers: true,
          autoCloseBrackets: true,
          matchBrackets: true,
          indentUnit: 2,
          tabSize: 2,
          lineWrapping: true,
          foldGutter: true,
          gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
          extraKeys: {
            "Ctrl-Space": "autocomplete",
            "Tab": "indentMore",
            "Shift-Tab": "indentLess"
          }
        });

        // 监听输入变化
        editor.on("change", function() {
          updateTsOutput();
        });
      });

      function jsonToTsInterface(input, interfaceName = "RootObject") {
        try {
          let obj;
          const inputType = document.getElementById("inputType").value;
          
          if (inputType === "json") {
            obj = JSON.parse(input);
          } else {
            try {
              obj = new Function('return ' + input)();
            } catch (e) {
              throw new Error("无效的JavaScript对象格式");
            }
          }

          let interfaces = new Map();
          let output = "";

          const generateType = (value, indent = 2, parentKey = "") => {
            const type = typeof value;
            if (type === "object") {
              if (value === null) return "null";
              if (Array.isArray(value)) {
                if (value.length === 0) return "any[]";
                const itemType = generateType(value[0], indent, parentKey);
                return `${itemType}[]`;
              }

              const newInterfaceName = `I${parentKey.charAt(0).toUpperCase() + parentKey.slice(1)}`;
              let nested = `{\n`;
              let nestedInterface = `interface ${newInterfaceName} {\n`;

              for (const key in value) {
                const propType = generateType(value[key], indent + 2, key);
                nested += `${" ".repeat(indent + 2)}${key}: ${propType};\n`;
                nestedInterface += `  ${key}: ${propType};\n`;
              }

              nested += `${" ".repeat(indent)}}`;
              nestedInterface += "}\n\n";

              if (Object.keys(value).length > 0) {
                interfaces.set(newInterfaceName, nestedInterface);
                return newInterfaceName;
              }
              return nested;
            }
            return type;
          };

          let mainInterface = `interface ${interfaceName} {\n`;
          for (const key in obj) {
            const value = obj[key];
            mainInterface += `  ${key}: ${generateType(value, 2, key)};\n`;
          }
          mainInterface += "}\n";

          for (const [_, interfaceStr] of interfaces) {
            output += interfaceStr;
          }
          output += mainInterface;

          return output;
        } catch (error) {
          return null;
        }
      }

      function updateTsOutput() {
        const jsonInput = editor.getValue();
        const interfaceName = document.getElementById("interfaceName").value;
        const errorMessage = document.getElementById("errorMessage");
        const tsOutput = document.getElementById("tsOutput");
        const inputType = document.getElementById("inputType").value;

        try {
          if (inputType === "json") {
            JSON.parse(jsonInput);
          } else {
            new Function('return ' + jsonInput)();
          }
          errorMessage.textContent = "";
        } catch (error) {
          errorMessage.textContent = `无效的${inputType === "json" ? "JSON" : "JavaScript对象"}格式: ${error.message}`;
          tsOutput.textContent = "";
          return;
        }

        if (jsonInput.trim()) {
          const result = jsonToTsInterface(jsonInput, interfaceName);
          tsOutput.textContent = result || "";
          hljs.highlightElement(tsOutput);
        } else {
          tsOutput.textContent = "";
        }
      }

      function copyToClipboard() {
        const tsOutput = document.getElementById("tsOutput");
        const text = tsOutput.textContent;
        navigator.clipboard.writeText(text).then(() => {
          const copyBtn = document.querySelector('.copy-button');
          copyBtn.textContent = '已复制！';
          setTimeout(() => {
            copyBtn.textContent = '复制代码';
          }, 2000);
        });
      }

      function beautifyInput() {
        const inputType = document.getElementById("inputType").value;
        
        try {
          if (inputType === "json") {
            let jsonStr = editor.getValue().replace(/'/g, '"');
            const obj = JSON.parse(jsonStr);
            editor.setValue(JSON.stringify(obj, null, 2));
          } else {
            const obj = new Function('return ' + editor.getValue())();
            editor.setValue(JSON.stringify(obj, null, 2));
          }
          updateTsOutput();
        } catch (error) {
          document.getElementById("errorMessage").textContent = `无效的${inputType === "json" ? "JSON" : "JavaScript对象"}格式: ${error.message}`;
        }
      }

      // 事件监听器
      document.getElementById("interfaceName").addEventListener("input", updateTsOutput);
      document.getElementById("inputType").addEventListener("change", function() {
        editor.setOption("mode", this.value === "json" ? "javascript" : "javascript");
        CodeMirror.autoLoadMode(editor, "javascript");
        updateTsOutput();
      });

      // 初始化代码高亮
      hljs.highlightAll();
    </script>
  </body>
</html>